/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.nanosn.framework.web.api;

import com.nanosn.framework.web.template.Template;
import com.nanosn.framework.core.Component;
import com.nanosn.framework.core.ComponentManager;
import com.nanosn.framework.core.ExtensionPoint;
import com.nanosn.framework.web.template.ITemplateProvider;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.TemplateException;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 *
 * @author selkhateeb
 */
public class ServletComponent extends HttpServlet
{

    // <editor-fold defaultstate="collapsed" desc="Private Fields">
    private ExtensionPoint extensionPoint;
    private Configuration freeMarkerconfiguration;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Init Methond">
    @Override
    public void init() throws ServletException
    {
        super.init();
        System.out.println("Init");
        ComponentManager cm = new ComponentManager(
                this.getServletContext().getRealPath("WEB-INF/classes"));

        try
        {
            this.extensionPoint = new ExtensionPoint(cm, IRequestHandler.class);
        }
        catch (Exception ex)
        {
            Logger.getLogger(ServletComponent.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        }

        this.freeMarkerconfiguration = new Configuration();
        try
        {
            this.freeMarkerconfiguration.setDirectoryForTemplateLoading(
                    new File(
                    this.getServletContext().getRealPath("templates")));
        }
        catch (IOException ex)
        {
            Logger.getLogger(ServletComponent.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        }
        this.freeMarkerconfiguration.setObjectWrapper(
                new DefaultObjectWrapper());
    }
    // </editor-fold>

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request,
                                  HttpServletResponse response)
            throws ServletException, IOException
    {
        for (Component component : this.extensionPoint.getComponents())
        {
            List<Class> interfaces = Arrays.asList(component.getClass().getInterfaces());

            //We are looking for templates
            if (interfaces.contains(ITemplateProvider.class))
            {
                //Get request uri
                String uri = getComponentRootURI(request.getRequestURI().replace(request.getContextPath() + request.getServletPath(), ""));

                //Get the template directory
                ITemplateProvider templateProvider = (ITemplateProvider) component;
                String templateDirectory = templateProvider.get_templates_dirs().get(uri);
                if(templateDirectory == null)
                {
                    //This means this is a static file -- Best guess :)
                    String staticFileDirectory = templateProvider.get_htdocs_dirs().get(uri);
                    if(staticFileDirectory != null)
                    {
                        //Cool we can handle this as a static file
                        HandleStaticFiles(uri,staticFileDirectory, request, response);
                        return;
                    }

                }
                this.freeMarkerconfiguration.setDirectoryForTemplateLoading(new File(this.getServletContext().getRealPath(File.separatorChar + templateDirectory)));

                IRequestHandler requestHandler = (IRequestHandler) component;
                if (requestHandler.MatchRequest(request))
                {
                    Template template = requestHandler.processRequest(request, response);
                    freemarker.template.Template t =
                            this.freeMarkerconfiguration.getTemplate(
                            template.getName());
                    try
                    {
                        t.process(template.getData(), response.getWriter());
                        response.getWriter().flush();
                    }
                    catch (TemplateException ex)
                    {
                        Logger.getLogger(ServletComponent.class.getName()).log(
                                Level.SEVERE, ex.getMessage(), ex);
                    }
                }
            }
        }
    }

    // <editor-fold defaultstate="collapsed" desc="Private Methods">
    private String getComponentRootURI(String requestURI)
    {
        ArrayList<String> uris = new ArrayList<String>(Arrays.asList(requestURI.split("/")));
        boolean containsEmptyString = true;
        //Remove blanks
        while (containsEmptyString)
        {
            containsEmptyString = uris.remove("");
        }
        return uris.get(0);
    }

    private void HandleStaticFiles(String uri, String realPath,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws ServletException, IOException
    {
        //TODO

        //Remove the one that matches the ServletPath
        ArrayList<String> requestUrlArray = new ArrayList<String>(Arrays.asList(request.getRequestURI().split("/")));
        //requestUrlArray.remove(request.getContextPath().replace("/", ""));
        requestUrlArray.remove(request.getServletPath().replace("/", ""));



        //Reassemble the uri
        String newUri = "";
        for (String string : requestUrlArray)
        {
            if(string.equals(uri))
                string = realPath;
            newUri += "/" + string;
        }
        newUri = newUri.substring(1);

        final HttpServletRequest a = request;
        final String b = newUri;
        HttpServletRequest r = new HttpServletRequest() {

            public String getAuthType()
            {
                return a.getAuthType();
            }

            public String getContextPath()
            {
                return a.getContextPath();
            }

            public Cookie[] getCookies()
            {
                return a.getCookies();
            }

            public long getDateHeader(String arg0)
            {
                return a.getDateHeader(arg0);
            }

            public String getHeader(String arg0)
            {
                return a.getHeader(arg0);
            }

            public Enumeration getHeaderNames()
            {
                return a.getHeaderNames();
            }

            public Enumeration getHeaders(String arg0)
            {
                return a.getHeaders(arg0);
            }

            public int getIntHeader(String arg0)
            {
                return a.getIntHeader(arg0);
            }

            public String getMethod()
            {
                return a.getMethod();
            }

            public String getPathInfo()
            {
                System.out.println(a.getPathInfo());
                //return a.getPathInfo();
                return b.replace(a.getContextPath(), "");
            }

            public String getPathTranslated()
            {
                return a.getPathTranslated();
            }

            public String getQueryString()
            {
                return a.getQueryString();
            }

            public String getRemoteUser()
            {
                return a.getRemoteUser();
            }

            public String getRequestURI()
            {
                return b;
            }

            public StringBuffer getRequestURL()
            {
                return new StringBuffer(b);
            }

            public String getRequestedSessionId()
            {
                return a.getRequestedSessionId();
            }

            public String getServletPath()
            {
                return a.getServletPath();
            }

            public HttpSession getSession()
            {
                return a.getSession();
            }

            public HttpSession getSession(boolean arg0)
            {
                return a.getSession(arg0);
            }

            public Principal getUserPrincipal()
            {
                return a.getUserPrincipal();
            }

            public boolean isRequestedSessionIdFromCookie()
            {
                return a.isRequestedSessionIdFromCookie();
            }

            public boolean isRequestedSessionIdFromURL()
            {
                return a.isRequestedSessionIdFromURL();
            }

            public boolean isRequestedSessionIdFromUrl()
            {
                return a.isRequestedSessionIdFromUrl();
            }

            public boolean isRequestedSessionIdValid()
            {
                return a.isRequestedSessionIdValid();
            }

            public boolean isUserInRole(String arg0)
            {
                return a.isUserInRole(arg0);
            }

            public Object getAttribute(String arg0)
            {
                return a.getAttribute(arg0);
            }

            public Enumeration getAttributeNames()
            {
                return a.getAttributeNames();
            }

            public String getCharacterEncoding()
            {
                return a.getCharacterEncoding();
            }

            public int getContentLength()
            {
                return a.getContentLength();
            }

            public String getContentType()
            {
                return a.getContentType();
            }

            public ServletInputStream getInputStream() throws IOException
            {
                return a.getInputStream();
            }

            public Locale getLocale()
            {
                return a.getLocale();
            }

            public Enumeration getLocales()
            {
                return a.getLocales();
            }

            public String getParameter(String arg0)
            {
                return a.getParameter(arg0);
            }

            public Map getParameterMap()
            {
                return a.getParameterMap();
            }

            public Enumeration getParameterNames()
            {
                return a.getParameterNames();
            }

            public String[] getParameterValues(String arg0)
            {
                return a.getParameterValues(arg0);
            }

            public String getProtocol()
            {
                return a.getProtocol();
            }

            public BufferedReader getReader() throws IOException
            {
                return a.getReader();
            }

            public String getRealPath(String arg0)
            {
                return a.getRealPath(arg0);
            }

            public String getRemoteAddr()
            {
                return a.getRemoteAddr();
            }

            public String getRemoteHost()
            {
                return a.getRemoteHost();
            }

            public RequestDispatcher getRequestDispatcher(String arg0)
            {
                return a.getRequestDispatcher(arg0);
            }

            public String getScheme()
            {
                return a.getScheme();
            }

            public String getServerName()
            {
                return a.getServerName();
            }

            public int getServerPort()
            {
                return a.getServerPort();
            }

            public boolean isSecure()
            {
                return a.isSecure();
            }

            public void removeAttribute(String arg0)
            {
                a.removeAttribute(arg0);
            }

            public void setAttribute(String arg0, Object arg1)
            {
                a.setAttribute(arg0, arg1);
            }

            public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException
            {
                a.setCharacterEncoding(arg0);
            }
        };
        RequestDispatcher rd = getServletContext().getNamedDispatcher("default");

        HttpServletRequest wrapped = new HttpServletRequestWrapper(r)
        {
            @Override
            public String getServletPath()
            {
                return "";
            }
        };
        rd.forward(wrapped, response);

    }
    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="HttpServlet Methods">

    /**
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    @Override
    protected void doDelete(HttpServletRequest request,
                            HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    @Override
    protected void doPut(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException
    {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo()
    {
        //TODO
        return "Short description";
    }
    // </editor-fold>
}
